﻿using System;
using System.Linq;
using Notoric.Commanding.CommandBase;
using LukasKopenec.LocalizationSupport;

namespace Notoric.ViewModel
{
    /// <summary>
    /// An undoable command for creating a new <see cref="Elements.Page"/> in a <see cref="Elements.Score"/>.
    /// </summary>
    /// <remarks>
    /// Input Parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="PageName"/> (<see cref="string"/>)
    /// </term>
    /// <description>
    ///     The name of the new page. Default value is an empty string ("").
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Number"/> (<see cref="int"/>)
    /// </term>
    /// <description>
    ///     The number of the new page. Default value is 0.
    /// </description>
    /// </item>
    /// <item>
    /// <term>
    ///     <see cref="Parent"/> (<see cref="Elements.Score"/>)
    /// </term>
    /// <description>
    ///     The score in which the new page will be created.
    /// </description>
    /// </item>
    /// </list>
    /// Output Parameters:
    /// <list type="table">
    /// <listheader>
    /// <term>
    ///     Name
    /// </term>
    /// <description>
    ///     Description
    /// </description>
    /// </listheader>
    /// <item>
    /// <term>
    ///     <see cref="CreatedPage"/> (<see cref="Elements.Page"/>)
    /// </term>
    /// <description>
    ///     The page created by this command.
    /// </description>
    /// </item>
    /// </list>
    /// The command can execute if the <see cref="Parent"/> is set to a non-null value.
    /// </remarks>
    public class NewPageCommand : UndoableCommandBase
    {
        #region Constructors

        /// <summary>
        /// Creates a new <see cref="NewPageCommand"/> instance.
        /// </summary>
        /// <param name="undoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The command stack where the command will be put after its execution.
        /// </para>
        /// </param>
        /// <param name="redoStack">
        /// <para>
        /// Type: <see cref="CommandStack"/>
        /// </para>
        /// <para>
        /// The command stack where the command will be put when it has been undone.
        /// </para>
        /// </param>
        public NewPageCommand(CommandStack undoStack, CommandStack redoStack)
            : base(undoStack, redoStack)
        {
            AddParameter<string>("PageName", ParameterDirection.In);
            AddParameter<int>("Number", ParameterDirection.In);
            AddParameter<Elements.Score>("Parent", ParameterDirection.In);
            AddParameter<Elements.Page>("CreatedPage", ParameterDirection.Out);

            PageName = "";
            Number = 0;

            Name = (string)LocalizationManager.GetValue("Commands.Names.NewPage");
        }

        #endregion

        #region Command Logic

        /// <inheritdoc/>
        protected override void CommandOperation(object parameter)
        {
            index = (Number > 0 ? Number - 1 : Parent.Pages.Count);
            var pagePart = ((Model.Packaging.ScorePart)Parent.AdaptedPart).CreatePagePart(PageName, index);
            virtualPath = ((Model.Elements.ScoreElement)Parent.AdaptedElement).Pages[pagePart.Uri];
            CreatedPage = Parent.Pages[index];

            Parent.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void RedoOperation()
        {
            Parent.AdaptedPart.Package.RestorePart(CreatedPage.AdaptedPart);
            ((Model.Elements.ScoreElement)Parent.AdaptedElement).Pages.Insert(virtualPath, CreatedPage.AdaptedPart.Uri, index);
            Parent.Pages[index] = CreatedPage;
            Parent.Children[index] = CreatedPage;

            Parent.FindOwner<Elements.Project>().IsModified = true;
        }

        /// <inheritdoc/>
        protected override void UndoOperation()
        {
            ProjectExplorerCommands.CloseCommand.Execute(CreatedPage, null);
            ((Model.Elements.ScoreElement)Parent.AdaptedElement).Pages.Remove(CreatedPage.AdaptedPart.Uri);
            Parent.AdaptedPart.Package.DeletePart(CreatedPage.AdaptedPart.Uri);

            Parent.FindOwner<Elements.Project>().IsModified = true;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the page created by this command.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.Page"/>
        /// </para>
        /// <para>
        /// The page created by this command.<br/>
        /// This property wraps the "CreatedPage" command parameter.
        /// </para>
        /// </value>
        public Elements.Page CreatedPage
        {
            get { return GetParameterValue<Elements.Page>("CreatedPage"); }
            private set { SetParameterValue<Elements.Page>("CreatedPage", value); }
        }

        /// <summary>
        /// Gets or sets the name of the new page.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="string"/>
        /// </para>
        /// <para>
        /// The name of the new page.<br/>
        /// The name is displayed in the Project Explorer and the user
        /// can use it for an easier orientation. This parameter is optional.
        /// There can be multiple pages with the same name or the page
        /// can have no name. Default value is an empty string ("").
        /// </para>
        /// <para>
        /// This property wraps the "PageName" command parameter.
        /// </para>
        /// </value>
        public string PageName
        {
            get { return GetParameterValue<string>("PageName"); }
            set { SetParameterValue<string>("PageName", value); }
        }

        /// <summary>
        /// Gets or sets the number of the new page.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Int32"/>
        /// </para>
        /// <para>
        /// The number of the new page inside the score.
        /// This value must be greater or equal to 0 and less than or equal to
        /// the number of pages in the score. If the value is 0 than the created
        /// page will be the last one in the score. Default value is 0.
        /// If a page with the same number already exists in the score, its number
        /// (and the numbers of all the following pages) is increased by one.
        /// </para>
        /// <para>
        /// This property wraps the "Number" command parameter.
        /// </para>
        /// </value>
        public int Number
        {
            get { return GetParameterValue<int>("Number"); }
            set { SetParameterValue<int>("Number", value); }
        }

        /// <summary>
        /// Gets or sets the score in which the new page will be created.
        /// </summary>
        /// <value>
        /// <para>
        /// Type: <see cref="Elements.Score"/>
        /// </para>
        /// <para>
        /// The score in which the new page will be created.<br/>
        /// This property wraps the "Parent" command parameter.
        /// </para>
        /// </value>
        public Elements.Score Parent
        {
            get { return GetParameterValue<Elements.Score>("Parent"); }
            set { SetParameterValue<Elements.Score>("Parent", value); }
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the index of the created page in the path collection.
        /// </summary>
        private int index;
        /// <summary>
        /// Holds the virtual path to the created page.
        /// </summary>
        private string virtualPath;

        #endregion
    }
}
